home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1995-06-12 | 73.3 KB | 2,377 lines
head 1.8; branch ; access ; symbols beta10:1.7; locks death:1.8; strict; comment @@; 1.8 date 93.04.04.23.44.26; author death; state Exp; branches ; next 1.7; 1.7 date 93.01.10.15.07.54; author death; state Exp; branches ; next 1.6; 1.6 date 92.07.26.13.58.06; author death; state Exp; branches ; next 1.5; 1.5 date 92.04.27.20.40.36; author death; state Exp; branches ; next 1.4; 1.4 date 92.04.05.22.51.36; author death; state Exp; branches ; next 1.3; 1.3 date 92.03.29.12.36.14; author death; state Exp; branches ; next 1.2; 1.2 date 92.03.29.12.29.06; author death; state Exp; branches ; next 1.1; 1.1 date 92.03.29.12.18.44; author death; state Exp; branches ; next ; desc @This is the implementation of the early draft of the file object. @ 1.8 log @Sun Apr 4 23:44:26 PDT 1993 @ text @/* ==================================================================== This is the implementation file for the File class. Full documentation for this class can be found in the File.rtf file. I will not duplicate all that fine information here. This is $Revision: 1.7 $ of this file It was last modified by $Author: death $ on $Date: 93/01/10 15:07:54 $ Note that this file was created while using the New Century Schoolbook Roman typeface. You may find that some things line up strangely if you don't use that family. * $Log: File.m,v $ Revision 1.7 93/01/10 15:07:54 death Sun Jan 10 15:07:54 PST 1993 Revision 1.6 92/07/26 13:58:06 death Update so all works with the font converter... Revision 1.4 92/04/05 22:51:36 death Miscelaneous revisions. This is the last version of version 1. Revision 1.3 92/03/29 12:36:14 death Bug in the open method. If we failed, we returned OK, if we succed, we reported failure. Revision 1.2 92/03/29 12:29:06 death Oops. Managed to check in the wrong version. this one has the code reflecting the new location of the init methods Revision 1.1 92/03/29 12:18:44 death Initial revision *==================================================================== */ // // Import our own definition // #import "File.h" #import <stdio.h> #import <string.h> #import <stdlib.h> #import <sys/param.h> // for maxpathlen #import <libc.h> // for getwd #import <sys/file.h> // for open() #import <streams/streams.h> // for streams stuff (exception codes) #include <sys/time.h> // for gettimeofday #if (NSmajor == 3) # import <errno.h> #endif @@implementation File // // @@@@@@ bugs... doesn't deal with links properly. =( // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: CreateAndOpenFor // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This method will try to create a new file and open it. The file is presumed // to be the one whose path is stored in the instance variables of this object. // If the file already exists, or if an error occurrs when trying to creat it, // an error is stored. Otherwise the file is opened, and the object is ready for use. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - CreateAndOpenFor: (AccessType) access { // // If the object is working with a file already give an error. // otherwise, do our stuff. // if (TheFile != NULL) [self StoreErrorCode: ERR_SOMEFILEOPEN AndText: "Object already in use for another file."]; { if ([self FileExists: FileName] == YES) [self StoreErrorCode: ERR_FILEEXISTS AndText: "Can't create the requested file. It already exists."]; else { [self CreateFile: FileName]; if ([self GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_CREATEFAIL AndText: "Can't create the requested file."]; else [self OpenWithAccess: access]; // Error info stored by this method. } } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: OpenExistingFor // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This method will try to open an existing file. The file is presumed // to be the one whose path is stored in the instance variables of this object. // If the file does not exist, an error is stored. Otherwise the file is opened, // and the object is ready for use. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - OpenExistingFor: (AccessType) access { // // If the object is working with a file already give an error. // otherwise, do our stuff. // if (TheFile != NULL) [self StoreErrorCode: ERR_SOMEFILEOPEN AndText: "Object already in use for another file."]; { if ([self FileExists: FileName] == NO) [self StoreErrorCode: ERR_FILEDOESNTEXIST AndText: "Can't locate the requested file."]; else [self OpenWithAccess: access]; // Error info stored by this method. } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ClearAndOpenExistingFor // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This method will try to open an existing fil, and then clear (truncate) it. // The file is presumed to be the one whose path is stored in the instance // variables of this object. If the file does not exist, or if an error occurrs while // clearing it, an error is stored. Otherwise the file is opened, // and the object is ready for use. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ClearAndOpenExistingFor: (AccessType) access { // // If the object is working with a file already give an error. // otherwise, do our stuff. // if (TheFile != NULL) [self StoreErrorCode: ERR_SOMEFILEOPEN AndText: "Object already in use for another file."]; { if ([self FileExists: FileName] == NO) [self StoreErrorCode: ERR_FILEDOESNTEXIST AndText: "Can't locate the requested file."]; else { [self ClearFile: FileName]; if ([self GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_CLEARFAIL AndText: "Can't clear the file!"]; else [self OpenWithAccess: access]; // Error info stored by this method. } } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ClearAndOpenFor // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This method will try to open a file. If it doesn't exist already, it will create it. // if it does, then it will clear it (truncate). The file is then opened if all went well // The file is presumed to be the one whose path is stored in the instance // variables of this object. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ClearAndOpenFor: (AccessType) access { // // If the object is working with a file already give an error. // otherwise, do our stuff. // if (TheFile != NULL) [self StoreErrorCode: ERR_SOMEFILEOPEN AndText: "Object already in use for another file."]; { if ([self FileExists: FileName] == NO) { [self CreateFile: FileName]; if ([self GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_CREATEFAIL AndText: "Can't create the requested file."]; } else { [self ClearFile: FileName]; if ([self GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_CLEARFAIL AndText: "Can't clear the file!"]; } // // The file exists and is of 0 length. open it now. // if ([self GetErrorCode] == ERR_OK) [self OpenWithAccess: access]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: OpenFor // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This method will try to open a file. If it doesn't exist already, it will create it. // The file is then opened if all went well. // The file is presumed to be the one whose path is stored in the instance // variables of this object. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - OpenFor: (AccessType) access { // // If the object is working with a file already give an error. // otherwise, do our stuff. // if (TheFile != NULL) [self StoreErrorCode: ERR_SOMEFILEOPEN AndText: "Object already in use for another file."]; { if ([self FileExists: FileName] == NO) { [self CreateFile: FileName]; if ([self GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_CREATEFAIL AndText: "Can't create the requested file."]; } else [self StoreErrorCode: ERR_OK AndText: "File exists. all is peachy."]; // // The file exists. open it now. // if ([self GetErrorCode] == ERR_OK) [self OpenWithAccess: access]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: OpenWithAccess: // Parameters: the means of accessing the specified file // Returns: self // Stores: none // Description: // This is a lower level opening routine. It should probably never be called from // the outside, but instead be called by the (presently) five opening routines // above this in the listing. // It's purpose is simple: open the file that this object is going to work with. // If the file does'nt exist, it returns an error. Thus, it is up to the caller to // assure that the file exists and is in th proper form before calling this. // Bugs: // Not accounting for the fact that I may not have *permission* (666 kinda thing) // to do any of these... ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - OpenWithAccess: (AccessType) operation { struct stat *statBuf; [self ResetResults]; // // Make sure that the file exists, and that we aren't already working with // an opened file. If all is good, open it appropriately // if (([self FileExists: FileName] == YES) && (TheFile == NULL)) { switch (operation) { case FILE_READ: TheFile = NXMapFile(FileName, NX_READONLY); break; case FILE_WRITE: TheFile = NXMapFile(FileName, NX_WRITEONLY); break; case FILE_READWRITE: TheFile = NXMapFile(FileName, NX_READWRITE); break; case FILE_APPEND: TheFile = NXMapFile(FileName, NX_WRITEONLY); // // Appending must be at the end of the file... // @@@@@@ do we need + 1 here? // [self MoveTo: [self FileSize]]; break; default: [self StoreErrorCode: ERR_BADACCESS AndText: "An invalid access operation was requested. No file opened."]; break; } } // // If the file wasn't opened, or some other problem arose, generate a negative reply. // if ((TheFile == NULL) && ([self GetErrorCode] != ERR_BADACCESS)) [self StoreErrorCode: ERR_CANTOPEN AndText: "The specified file could not be opened (bad permissions? Bad path?)"]; else { AccessMode = operation; // // Store the real inode now, in case we could not get it during the init. // [self FileInfo]; if ([self GetErrorCode] != ERR_OK) TheInode = 0; else { statBuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT]; TheInode = statBuf->st_ino; } } // // Store whether the file was opened or not // if ((TheFile != NULL) && ([self GetErrorCode] == ERR_OK)) { FileIsOpen = YES; FileLocation = fileAtStart; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: initAndUse: // Parameters: none // Returns: self // Stores: none // Description: // This funky method is a pretty standard init method. It sets up all our instance // variables, and does nothing more. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - initAndUse: (roCString) pathname { struct stat *statbuf; [super init]; FileName = [self GetFullPathFrom: pathname]; AccessMode = FILE_NOACCESS; TheFile = NULL; // // Get our inode, if possible. // [self FileInfo]; if ([self GetErrorCode] != ERR_OK) { // @@@@@@ Maybe this shouldn't be this way... [self StoreErrorCode: ERR_OK AndText: "Hmmmmmm"]; TheInode = 0; } else { statbuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT]; TheInode = statbuf->st_ino; } FileIsOpen = NO; FileLocation = fileAtStart; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: initAndUseTemporary // Parameters: none // Returns: self // Stores: none // Description: // This method is used when one wishes to have a temporary file to use. // Calling this method will cause a temporary file to be created somwhere. // one can then use it as a completely ordinary file with this object. // Bugs: // At the moment, this is implemented with the tmpname call. Because we // ignore the TMP_MAX constant (the object shouldn't be tracking how many // temp files are open in the program, and the program shouldn't know we're // making use of tmpnam), we run the risk of opening too many temp files. // Thus, this should be re-implemented ourselves sometime (perhaps to: // /tmp/FileObj_TempXXXXXXXX, where X is a number-thing that will actually // be a date from a date object, or some something more unique... // History // 93.01.03 djb Added call to stat, because it seems you have to call tmpnam repeatedly // to get your unique name. YICK! Please move to the above. Then // replaced with intermediate hack using a pretty unique name using the // above and the process id, seconds since 1970, and microseconds. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - initAndUseTemporary { struct timeval tp; struct timezone tzp; [super init]; FileName = NewCString(63); gettimeofday(&tp, &tzp); // 93.01.24 djb Added the 'long' designation to the %d's at the NS3 compiler's suggestion. sprintf(FileName, "/tmp/TempFileDataP%dS%ldU%ld", getpid(), tp.tv_sec, tp.tv_usec); /* This approach does not work. Nor do previsu simpler ones. FileName = NewCString(L_tmpnam); ctr = 0; do { FileName = tmpnam(FileName); result = stat(FileName, &mystat); ctr++; } while ((errno != 2) && (ctr < TMP_MAX)); */ AccessMode = FILE_NOACCESS; TheFile = NULL; FileIsOpen = NO; FileLocation = fileAtStart; return self; } //============================================================ //============================================================ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ClearFile: // Parameters: a CString that contains a path to a file // Returns: self // Stores: none // Description: // This little kludgey method just clears the specified file. If anything bad // happens, it stores an error code and text. At the moment, it truncates the file // by opening it with the O_TRUNC flag to the open() routine. yech. This // implementation had BETTER remain transparent to this and all subclasses, // for it's sure to change at some point! // Bugs: // Ickly implementation. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ClearFile: (CString) pathname { int fd; errno = 0; fd =open(pathname, O_RDONLY|O_TRUNC, 666); if (errno != 0) [self StoreErrorCode: ERR_CLEARFAIL AndText: "The open() routine didn't O_TRUNC it!"]; close (fd); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: CreateFile: // Parameters: a CString that contains a path to a file // Returns: self // Stores: none // Description: // This little kludgey method just creates the specified file. If anything bad // happens, it stores an error code and text. At the moment, it creates the file // by opening it with the O_CREAT flag to the open() routine. yech. This // implementation had BETTER remain transparent to this and all subclasses, // for it's sure to change at some point! // Bugs: // Ickly implementation. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - CreateFile: (CString) pathname { int fd; errno = 0; fd =open(pathname, O_RDONLY|O_CREAT, 0666); if (errno != 0) [self StoreErrorCode: ERR_CREATEFAIL AndText: "The open() routine didn't create it!"]; else [self StoreErrorCode: ERR_OK AndText: "The file was created"]; close (fd); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: FileExists: // Parameters: a CString that contains a path to a file // Returns: True if the file exists // Stores: the return value // Description: // This method has only one purpose: it returns YES if the specified file // exists, and NO otherwise. It does this in a kludgey kinda fashion. Try to // open the file (read only). if we don't get an error, it must exist. otherwise, // it must not exist. close the file, and return the value. // Bugs: // There's got to be a better way... // Also, we always return an ERR_OK value... certianly *something* can go wrong? // Probably should be using stat??? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) FileExists: (CString) pathname { int fd; Integer temp; errno = 0; fd = open(pathname, O_RDONLY, 666); temp = errno; close (fd); if (temp != 0) { [self StoreErrorCode: ERR_OK AndText: "The file does not exist"]; return NO; } else { [self StoreErrorCode: ERR_OK AndText: "The file does indeed exist"]; return YES; } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetFullPathFrom // Parameters: a CString that contains a full or partial path to a file // Returns: a new CString containing the full path to the file // Stores: the new CString // Description: // The purpose of this string is to take a full or partial pathname (i.e. // /me/Library/file.ext or Library/file.ext or file.ext), and return the full // pathname to the file. // If we were passed a full pathname (starts with the / character), then we // make a full copy of it. // otherwise, use the function getwd to get the path to the current directory, // and fuse the two together. // Note that we do not reset the results here. Since this is an internal // method, the caller, in the object, may not want things cleared. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetFullPathFrom: (roCString) pathname { char rootedpath[MAXPATHLEN]; CString fullpath; char* pathresult; // // If the path is a complete path, then just copy it literally. // if (pathname[0] == '/') { fullpath = (CString) malloc(strlen(pathname) + 1); strcpy(fullpath, pathname); [self StoreErrorCode: ERR_OK AndText: "Got filename!"]; } else { // // Otherwise, we have a partial path. Get path to our current location // (presumably where the partial path starts from), and fuse the two // (if getwd() returns an error, something is wrong, and so we risk returning // just the partial path) // pathresult = getwd(rootedpath); if (*pathresult != 0) { fullpath = (CString) malloc(strlen(rootedpath)+1+strlen(pathname) + 1); sprintf(fullpath, "%s/%s", rootedpath, pathname); [self StoreErrorCode: ERR_OK AndText: "Got filename!"]; } else { fullpath = (CString) malloc(strlen(pathname) + 1); strcpy(fullpath, pathname); [self StoreErrorCode: ERR_STRANGEPATH AndText: "Pathname may be corrupted!"]; } } return fullpath; } //============================================================ //============================================================ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: Close // Parameters: a flag to indicate whether we should save the file // Returns: self // Stores: none // Description: // This closes the file, discarding any changes. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Close { [self ResetResults]; if (TheFile != NULL) { NXCloseMemory(TheFile, NX_FREEBUFFER); [self StoreErrorCode: ERR_OK AndText: "Closed right up"]; } else [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"]; FileIsOpen = NO; FileLocation = fileAtStart; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: CloseAndSave // Parameters: none // Returns: self // Stores: none // Description: // This closes the file,saving its contents before doing so. // Bugs: // how about some checking to make sure we dumped it all OK?? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - CloseAndSave { [self ResetResults]; if (TheFile != NULL) { if (AccessMode != FILE_READ) NXSaveToFile(TheFile, FileName); NXCloseMemory(TheFile, NX_FREEBUFFER); [self StoreErrorCode: ERR_OK AndText: "Closed right up"]; } else [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"]; FileIsOpen = NO; FileLocation = fileAtStart; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: CloseAndDelete // Parameters: none // Returns: self // Stores: the error we got back from the system routine. (don't depend on this) // Description: // This closes the file, and then deletes it. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - CloseAndDelete { Integer status; [self ResetResults]; [self Close]; status = remove(FileName); if (status != 0) { [self StoreErrorCode: ERR_CANTDELETE AndText: "Could not delete file. unknown why"]; [self StoreInteger: status]; } else [self StoreErrorCode: ERR_OK AndText: "Deleted the file just fine"]; FileIsOpen = NO; FileLocation = fileAtStart; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: free: // Parameters: none // Returns: self // Stores: none // Description: // This frees the object, which involves closing our connection to the physical // file, WITHOUT SAVING, and then disposing of our instance variables, // and then freeing our parent. // Bugs: // The call to close is commented out here because it's causing trouble if you // say: [myFile CloseAndSave] ; [myFile free]; Streams problem. we SHOULD // be somehow nulling the stream, in the Close methods, and closing here only if // it isn't null. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - free { //[self Close]; free(FileName); return [super free]; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: AdvanceBytes: // Parameters: the number of bytes we should advance // Returns: self // Stores: none // Description: // This changes our current position in the file by advancing us up a specified number // of bytes in it. We add an exception handler partially for the ease so we don't // have to do the error checking ourselves. Partially because it allows us to // check for other exceptions and kinda trap for them. And partially for the // novelty of it. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - AdvanceBytes: (FilePosDelta) byteLoc; { [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "The file wasn't even open!"]; else { // // Try to advance the requested number of bytes. If an exception occurrs, // and it's a seek error, just stick us at the end quietly. Otherwise, store // an error code and leave things as they are. // NX_DURING NXSeek(TheFile, byteLoc, NX_FROMCURRENT); [self StoreErrorCode: ERR_PEACHY AndText: "We found what we NXsaught."]; NX_HANDLER switch(NXLocalHandler.code) { case NX_illegalSeek: NXSeek(TheFile, 0, NX_FROMEND); [self StoreErrorCode: ERR_OK AndText: "Moved to the end."]; FileLocation = fileAtEOF; break; default : [self StoreErrorCode: ERR_BADADVANCE AndText: "A unknown exception occurred when advancing."]; break; } NX_ENDHANDLER } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: BackupBytes: // Parameters: the number of bytes we should backup // Returns: self // Stores: none // Description: // This changes our current position in the file by backing us up a specified number // of bytes in it. We set up an exception handler to deal with the case if the // user tries to seek before the beginning. We could, perhaps, do the traping // ourselves (compute current loc, distance from beginning, whether the byteLoc // falls within that request... etc), but this seems easier, and deals with any other // unexpected exceptions too. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BackupBytes: (FilePosDelta) byteLoc; { [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "The file wasn't even open!"]; else { // // Try to backup the requested number of bytes. If an exception occurrs, // and it's a seek error, just stick us at the beginning quietly. Otherwise, store // an error code and leave things as they are. // NX_DURING NXSeek(TheFile, -byteLoc, NX_FROMCURRENT); [self StoreErrorCode: ERR_OK AndText: "We found what we NXsaught."]; NX_HANDLER switch(NXLocalHandler.code) { case NX_illegalSeek: NXSeek(TheFile, 0, NX_FROMSTART); [self StoreErrorCode: ERR_OK AndText: "Moved to beginning."]; FileLocation = fileAtEOF; break; default : [self StoreErrorCode: ERR_BADBACKUP AndText: "A unknown exception occurred when backing up."]; break; } NX_ENDHANDLER } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetBasename // Parameters: none // Returns: the copy of a string containing the current filename's basename // Stores: A pointer to the same string we return // Description: // This simply extracts the basename of the current file name. The basename is // the part of the name that comes after thedirectory names, but before the extension. // If we are using the file /foo/bar/hotfile.rtf, this returns hotfile // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetBasename { CString lastSlash, lastDot, tempName, finish, start; Integer nameSize; [self ResetResults]; // // Determine the length of the basename by figuring out where its start is // (either right after the /, or at the start) and the end (before . or at the end) // lastSlash = strrchr(FileName, '/'); lastDot = strrchr(FileName, '.'); if (lastSlash == NullCString) start = FileName; else start = &lastSlash[1]; if ((lastDot == NullCString) || (lastDot < lastSlash)) finish = &FileName[strlen(FileName)]; else finish = lastDot; nameSize = finish - start; // // Allocate the proper space for the basename, store the text, and return it. // tempName = (CString) malloc(nameSize+1); strncpy(tempName, start, nameSize); tempName[nameSize] = EndOfCString; [self StoreErrorCode: ERR_OK AndText: "Located whatever basename was to be found."]; [self StoreCString: tempName]; return tempName; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetCurrentPosition // Parameters: none // Returns: A position in the file // Stores: A position in the file as an Integer // Description: // this returns a FilePos value which indicates a current position in the file, as // a count of the number of bytes since the beginning. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (FilePos) GetCurrentPosition { FilePos pos; [self ResetResults]; if (TheFile == NULL) { [self StoreErrorCode: ERR_FILENOTOPEN AndText: "No position in an unopened file."]; pos = 0; } else { pos = NXTell(TheFile); [self StoreErrorCode: ERR_OK AndText: "Got the file position."]; } [self StoreInteger: pos]; return pos; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetExtension // Parameters: none // Returns: the copy of a string containing the current filename's extension // Stores: A pointer to the same string we return // Description: // This simply extracts the extension of the current file name. The extension is // the part of the name that comes after the last . in the filename. If there is no // extension, this will simply return an empty string. // If we are using the file /foo/bar/hotfile.rtf, this returns rtf // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetExtension { // // Determine where the last / and . are. We need the / to be sure we don't get a . further // up in the path somewhere. // CString lastSlash, lastDot, start, tempName; [self ResetResults]; // // Locate the last . in the file name, and the last /. If we found no dot, or it came // before the slash in the name, then there is no extension, so start with a null CString. // otherwise, set the start of the exension to just after the last dot. // lastSlash = strrchr(FileName, '/'); lastDot = strrchr(FileName, '.'); if ((lastDot == NullCString) || (lastDot < lastSlash)) start = NullCString; else start = &lastDot[1]; // // Allocate space for the extension in a temporary string, and copy it into // TempName. // tempName = (CString) malloc(strlen(start)+1); strcpy(tempName, start); [self StoreErrorCode: ERR_OK AndText: "Located whatever extension was to be found."]; [self StoreCString: tempName]; return tempName; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetFilename // Parameters: none // Returns: the copy of a string containing the full filename, without // Stores: A pointer to the same string we return // Description: // this returns the path to the file that we are using without including the file name // itselfor the slash before the filename. If there are no /'s (and thus, oddly, no directories // refered to), a null string is returned. If we are using the file /foo/bar/hotfile.rtf, this // returns hotfile.rtf // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetFilename { CString start; CString tempName; [self ResetResults]; // // Locate the slash before the Filename, and make start point after it // (which happens to be the start of our filename) // start = strrchr(FileName, '/'); if (start != NullCString) start = &start[1]; else start = FileName; // // Allocate the proper space for the filename, copy the text, and return it. // tempName = (CString) malloc(strlen(start)+1); strcpy(tempName, start); [self StoreErrorCode: ERR_OK AndText: "Extracted the filename"]; [self StoreCString: tempName]; return tempName; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetDirectory // Parameters: none // Returns: the copy of a string containing the pathname to this directory // Stores: A pointer to the same string we return // Description: // this returns the path to the file that we are using without including the file name // itselfor the slash before the filename. If there are no /'s (and thus, oddly, no directories // refered to), a null string is returned. If we are using the filen /foo/bar/hotfile.rtf, this returns // /foo/bar // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetDirectory { // // Declare a temporary string to store the file name, clear the results, and copy // the actual file name into the temp string. // CString lastSlash; CString tempName = (CString) malloc(strlen(FileName)+1); [self ResetResults]; strcpy(tempName, FileName); // // Locate the last slash in the path name. If we find it, make it the end of the string instad, // otherwise clear the whole string. Return it in any case. // lastSlash = strrchr(tempName, '/'); if (lastSlash != NullCString) { lastSlash[0] = EndOfCString; [self StoreErrorCode: ERR_OK AndText: "Got the directory"]; } else { tempName[0]=EndOfCString; [self StoreErrorCode: ERR_NODIRECTORY AndText: "There were no directory names"]; } [self StoreCString: tempName]; return tempName; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: GetPathname: // Parameters: none // Returns: the copy of a string containing the pathname to this file // Stores: A pointer to the same string we return // Description: // This merely makes a copy of the string that we have stored for the pathname // and returns it to the caller. Given the filename /foo/bar/hotfile.rtf, this returns // /foo/bar/hotfile.rtf // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) GetPathname { CString tempName; [self ResetResults]; // // Allocate the proper space for the pahtname, store the text, and return it. // tempName = (CString) malloc(strlen(FileName)+1); strcpy(tempName, FileName); [self StoreErrorCode: ERR_OK AndText: "Gotcha your path"]; [self StoreCString: tempName]; return tempName; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: MoveTo: // Parameters: the byte position we should move to in the file // Returns: self // Stores: The position we moved to // Description: // This changes our position in the file to the position byteLoc bytes from the // beginning of the file. The new position in the file is stored before we return. // Deal with exceptions being raised around the NXSeek code. If an error occurs, // then we will assume that it's because we tried to move past the eof, and so we // set the position at the end of a file. if an unknown exception occurs, do nothing. // Bugs: // I don't know if the assumption that an NX_illegalSeek exception necessarily means // we tried to go past the end of the stream. If not, there's problems here. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (id) MoveTo: (PositiveInteger) byteLoc { Boolean result; [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "The file wasn't even open! How d'ya expect me to move around in it?!"]; else if (AccessMode == FILE_APPEND) [self StoreErrorCode: ERR_FILEAPPENDONLY AndText: "Can not change position in a file set for only appending"]; else { // // Try to move the requested number of bytes. If an exception occurrs, // and it's a seek error, just stick us at the end quietly (assume the // error was due to running over the end. Otherwise, store // an error code and leave things as they are. // NX_DURING NXSeek(TheFile, byteLoc, NX_FROMSTART); [self StoreErrorCode: ERR_OK AndText: "Moved OK"]; NX_HANDLER switch(NXLocalHandler.code) { case NX_illegalSeek: NXSeek(TheFile, 0, NX_FROMEND); [self StoreErrorCode: ERR_OK AndText: "Moved to the end."]; FileLocation = fileAtEOF; break; default : [self StoreErrorCode: ERR_BADADVANCE AndText: "A unknown exception occurred when advancing."]; break; } NX_ENDHANDLER result = NXAtEOS(TheFile); if (result != NO) FileLocation = fileAtEOF; else if ( NXTell(TheFile) == 0) FileLocation = fileAtStart; else FileLocation = fileInMiddle; [self StorePositiveInteger: NXTell(TheFile)]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: ReadByte // Parameters: none // Returns: the byte we read // Stores: The byte we read // YES if EOF was encountered // Description: // This reads a byte from our file. If all goes well, we return the byte. Otherwise, // we return 0 and store an error // Bugs: // BOOL NXAtEOS(NXStream *stream) // Says it should not be used on streams open for writing. What about read/write? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Byte) ReadByte { Byte byteIn = 0; Boolean result; [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND)) [self StoreErrorCode: ERR_FILEWRITEONLY AndText: "Can not read from a file set for only appending or writing"]; else { result = NXAtEOS(TheFile); if (result != NO) { [self StoreErrorCode: ERR_EOF AndText: "End of file found"]; FileLocation = fileAtEOF; [self StorePositiveInteger: 0]; [self PutBoolean: YES Into: SECOND_RESULT]; } else { byteIn = NXGetc(TheFile); [self StoreErrorCode: ERR_OK AndText: "Read the byte"]; [self StorePositiveInteger: byteIn]; [self PutBoolean: NO Into: SECOND_RESULT]; } } return byteIn; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: Read:BytesInto: // Parameters: the number of bytes we are to read // the buffer that we are to read info // Returns: self // Stores: The number of bytes we succesfully read // YES if we found EOF at the end of the current read // Description: // This attempts to read in numBytes bytes from our file into the buffer. It does no // checking to assure that the buffer is of the proper size. After reading the bytes, it // stores the number of bytes it succeeded in reading. If the number of bytes written // is not equal to the number requested, an error is stored. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (id) Read: (PositiveInteger) numBytes BytesInto: (ByteString) buffer { PositiveInteger BytesRead; Boolean result; [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND)) [self StoreErrorCode: ERR_FILEWRITEONLY AndText: "Can not read from a file set for only appending or writing"]; else { result = NXAtEOS(TheFile); if (result != NO) { [self StoreErrorCode: ERR_EOF AndText: "End of file found"]; FileLocation = fileAtEOF; [self StorePositiveInteger: 0]; [self PutBoolean: YES Into: SECOND_RESULT]; } else { BytesRead = NXRead(TheFile, buffer, numBytes); // // Deal with the consequences of our actions // if (BytesRead != numBytes) [self StoreErrorCode: ERR_READINGERROR AndText: "Read the wrong number of bytes"]; else [self StoreErrorCode: ERR_OK AndText: "Read the bytes."]; [self StorePositiveInteger: BytesRead]; [self PutBoolean: NO Into: SECOND_RESULT]; } } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: WriteByte // Parameters: the byte we are to write // Returns: self // Stores: The byte we think we wrote // Description: // This writes a specified byte to our file. It then checks to make sure we wrote // that byte. If we failed to do that, we generate an error. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (id) WriteByte: (Byte) theByte { PositiveInteger byteOut; [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if (AccessMode == FILE_READ) [self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"]; else { byteOut = NXPutc(TheFile, theByte); if (byteOut == theByte) [self StoreErrorCode: ERR_OK AndText: "Wrote the byte"]; else [self StoreErrorCode: ERR_WROTEBADCHAR AndText: "Wrote the wrong byte"]; [self StorePositiveInteger: byteOut]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Routine: Write:BytesFrom: // Parameters: the number of bytes we are to write // the buffer that we are to write from // Returns: self // Stores: The number of bytes we succesfully wrote // Description: // This writes a specified number of bytes from the buffer we have been passed to // the file that we manipulate. If we fail to write all the bytes requested, we store an // error. We always store the number of bytes we wrote. // Bugs: // We never flush the data out (NXFlush does naught for a memory stream) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (id) Write: (PositiveInteger) numBytes BytesFrom: (ByteString) buffer { PositiveInteger bytesWrote; [self ResetResults]; if (TheFile == NULL) [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if (AccessMode == FILE_READ) [self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"]; else { bytesWrote = NXWrite(TheFile, buffer, numBytes); // // Deal with the consequences of our actions // if (bytesWrote != numBytes) [self StoreErrorCode: ERR_WRITINGERROR AndText: "Wrote the wrong number of bytes"]; else [self StoreErrorCode: ERR_OK AndText: "Wrote the bytes."]; [self StorePositiveInteger: bytesWrote]; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: FileSize // Parameters: none // Returns: An integer reflecting the size of the file. Set to 0 if an error occured // Stores: The return value, if all went well, otherwise nothing // Description: // This determines how many bytes long the file that this object manipulates is, // and returns this number to the caller. // Bugs: // Only usable if the file has been opened... ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (PositiveInteger) FileSize { PositiveInteger currentLoc, eofLoc; [self ResetResults]; if (TheFile == NULL) { [self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened."]; eofLoc = 0; [self StorePositiveInteger: 0]; } else { currentLoc = NXTell(TheFile); NXSeek(TheFile, 0, NX_FROMEND); eofLoc = NXTell(TheFile); NXSeek(TheFile, currentLoc, NX_FROMSTART); [self StoreErrorCode: ERR_OK AndText: "Obtained size of file."]; [self StorePositiveInteger: eofLoc]; } return eofLoc; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: FileInfo // Parameters: none // Returns: self // Stores: the access mode is stored in return position 1 // a pointer to an OS-dependant structure is in 2 // Description: // This returns information to the caller that isn't accessable through other // methods. At this point, it returns the access method for the file, currently being // used. It also returns a pointer to a stat structure (see the man page for stat). // The stat structure may be replaced by a better structure in a future // version // Bugs: // info like file size won't be accurate to the caller. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - FileInfo { struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat)); if (stat(FileName, statbuf) != 0) [self StoreErrorCode: ERR_CANTGETINFO AndText: "Could not get machine dep. info"]; else { [self StoreErrorCode: ERR_OK AndText: "The info is yours!"]; [self PutInteger: AccessMode Into: FIRST_RESULT]; [self CopyPointer: (Pointer) statbuf WithLength: sizeof(struct stat) Into: SECOND_RESULT]; } free((char*) statbuf); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SameFileAs // Parameters: the path to another file // Returns: A boolean value. yes indicates that the pathname refers to the same // file this object is accessing. no means either they are different or // the other file does not exist. (or that this one doesn't exist) // Stores: This boolean. // Description: // Used to determine if some other file is the same as this one.. Pass the name of // the other file with a path, and this will check to see it if it refers to the same // file that it is using. Note that this doesn't understand symbolic links, really. // Bugs: // Inadequately deals with the negative cases where one or both files might not // actually exist. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) SameFileAs: (CString) someOtherFile { struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat)); CString pathToFile = [self GetFullPathFrom: someOtherFile]; ErrorCode result; Boolean answer = NO; if (stat(pathToFile, statbuf) != 0) result = -1; else { if (TheInode == statbuf->st_ino) result = 0; else result = 1; } FreeCString(pathToFile); // 92.01.01 djb Added freeing of statbuf to remove a memory leak. free((char*) statbuf); switch (result) { case -1: [self StoreErrorCode: ERR_OK AndText: "The file does not even exist!"]; answer = NO; break; case 0: [self StoreErrorCode: ERR_OK AndText: "The info is yours!"]; answer = YES; break; case 1: [self StoreErrorCode: ERR_OK AndText: "The info is yours!"]; answer = NO; break; } [self StoreBoolean: answer]; return answer; } @@end@ 1.7 log @Sun Jan 10 15:07:54 PST 1993 @ text @d4 2 a5 2 This is $Revision: 1.6 $ of this file It was last modified by $Author: death $ on $Date: 92/07/26 13:58:06 $ d8 3 d412 2 a413 1 sprintf(FileName, "/tmp/TempFileDataP%dS%dU%d", @ 1.6 log @Update so all works with the font converter... @ text @d4 2 a5 2 This is $Revision: 1.4 $ of this file It was last modified by $Author: death $ on $Date: 92/04/05 22:51:36 $ d8 4 d39 5 d395 5 d403 2 d406 19 a424 1 FileName = tmpnam(NULL); d1043 1 d1078 7 d1086 1 a1102 1 // We don't yet write the EOF flag. d1109 1 d1120 15 a1134 4 byteIn = NXGetc(TheFile); [self StoreErrorCode: ERR_OK AndText: "Read the byte"]; [self StorePositiveInteger: byteIn]; [self PutBoolean: NO Into: SECOND_RESULT]; a1151 1 // We don't watch for the EOF value yet... //@@@@@@@@@@@@@@@@ d1156 1 d1167 8 a1174 6 BytesRead = NXRead(TheFile, buffer, numBytes); // // Deal with the consequences of our actions // if (BytesRead != numBytes) [self StoreErrorCode: ERR_READINGERROR AndText: "Read the wrong number of bytes"]; d1176 12 a1187 3 [self StoreErrorCode: ERR_OK AndText: "Wrote the bytes."]; [self StorePositiveInteger: BytesRead]; [self PutBoolean: NO Into: SECOND_RESULT]; d1366 2 @ 1.5 log @misc changes (intermediate version). @ text @d27 2 a28 2 #import "Reply.h" #import <stdio.h> // for fopen, among others d32 3 a34 1 #import <libc.h> //for getwd d37 3 d41 38 d80 13 a92 1 char strrpos(char* s, char c) d94 17 a110 7 Cstring result; result = rindex(s, c); if (result == NULL) return -1; else return (result - s); d113 38 a152 2 // General comment: // If errors occurred when starting up, nothing is stored d154 13 a166 10 /********************************************************************************** When called, this opens the specified file, if possible, and sets up the object. Bugs: too minimal. Header comment sucks. For religious as well as sanity reasons, I dislike multiple exit points. Thi is just to poin out that there are three returns in here. There should be one... mayhaps allocate the reply objects, and then return whichever got allocated at the end... Can we free ourselves safely at the end if we fail?? =( **********************************************************************************/ - (id) openFile: (const char *) workingName For: (long int) operation a167 8 id result = NULL; FILE* tempfd; char pathname[MAXPATHLEN]; int pathresult; [super init]; /* We should make the workingName a full pathname if it isn't already... */ d169 2 a170 1 // copy the string into our instance variable, and store the operation requested d172 3 a174 1 if (workingName[0] != '/') d176 1 a176 2 pathresult = getwd(pathname); if (pathresult != 0) d178 4 a181 2 fileName = (Cstring) malloc(strlen(pathname)+1+strlen(workingName) + 1); sprintf(fileName, "%s/%s", pathname, workingName); d183 1 a183 1 else // we may be in trouble, so just use the file name by default d185 4 a188 2 fileName = (Cstring) malloc(strlen(workingName) + 1); strcpy(fileName, workingName); d190 5 d196 25 a220 1 else // path is rooted, so just use it literally... d222 14 a235 2 fileName = (Cstring) malloc(strlen(workingName) + 1); strcpy(fileName, workingName); d237 26 a262 2 accessType = operation; theFile = NULL; d264 2 a265 1 // Open the file, reflecting the access method we want to have. d267 1 a267 1 switch (operation) d269 24 a292 31 case FILE_READ: theFile = NXMapFile(fileName, NX_READONLY); break; case FILE_WRITE: // try to create the file, or clear it. If that works, close it, and open it as a stream. //Note for future versions: Maybe there's a nice NX..stream thing to take care of this. tempfd = fopen(fileName, "w+"); if (tempfd !=NULL) { fclose(tempfd); theFile = NXMapFile(fileName, NX_WRITEONLY); } // should generate an error reply here break; case FILE_READWRITE: tempfd = fopen(fileName, "w+"); if (tempfd !=NULL) { fclose(tempfd); theFile = NXMapFile(fileName, NX_READWRITE); } // should generate an error reply here break; /* APPENDING not supported in current version... =( case FILE_APPEND theFile = fopen(fileName, "a"); break; */ default: result = [[Reply alloc] initErrorWithCode: ERR_BADACCESS Text: "An invalid access operation was requested. No file opened."]; break; a295 1 // otherwise, generate a positive one. d297 3 a299 4 if (theFile == NULL) // BUG: We overwrite the text generated in default, above!! result = [[Reply alloc] initErrorWithCode: ERR_CANTOPEN Text: "The specified file could not be opened (bad permissions? Bad path?)"]; d301 24 a324 5 result = [[Reply alloc] initReplyWithCode: ERR_PEACHY Text: "I found no errors (didn't look neither) so all must be OK." Data: (void*) self Type: TYPE_OBJECT]; return result; d328 11 a338 4 /********************************************************************************** This simply calls openFile:For: with a default operation value. **********************************************************************************/ - (id) openFile: (const char *) filename d340 26 a365 1 return [self openFile: filename For: FILE_READWRITE]; d369 19 a387 6 /********************************************************************************** This is a dummy routine provided to provide support for NeXT style standard methods (i.e. objects havng an 'init...' method). It just calls the real init method (which I enourage you to use because of its better name =), openFile:For: **********************************************************************************/ - (id) initFile: (const char*) workingName For: (long int) operation d389 7 a395 1 return [self openFile: workingName For: operation]; d399 19 a417 4 /*============================================================*\ Close the file, deallocate the string value, and free the object. \*============================================================*/ - (id) closeFile d419 8 a426 1 return [self close]; // get rid of this method, dude d429 16 a444 4 /*============================================================*\ Close the file, deallocate the string value, and free the object. \*============================================================*/ - (id) close d446 38 a483 1 if (theFile != NULL) d485 2 a486 3 // check return NXSaveToFile(theFile, fileName); NXCloseMemory(theFile, NX_FREEBUFFER); d488 5 a492 2 free(fileName); return [super free]; d495 1 d497 15 a511 3 // Routine: advanceBytes: // Parameters: the number of bytes to advance in the file // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) d513 1 a513 1 - (id) advanceBytes: (FilePosDelta) byteLoc; d515 13 a527 5 id result; if (theFile == NULL) result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "The file wasn't even open! How d'you expect me to move around in it?!"]; d530 19 a548 3 NXSeek(theFile, byteLoc, NX_FROMCURRENT); result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "We found what we NXsaught."]; d550 1 a550 1 return result; d553 2 d558 7 a564 3 // Routine: backupBytes: // Parameters: the number of bytes to advance in the file // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) d566 1 a566 1 - (id) backupBytes: (FilePosDelta) byteLoc; d568 61 a628 1 id result; d630 7 a636 3 if (theFile == NULL) result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "The file wasn't even open! How d'you expect me to move around in it?!"]; d638 52 d691 22 a712 3 NXSeek(theFile, -byteLoc, NX_FROMCURRENT); result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "We found what we NXsaught."]; d714 1 a714 1 return result; d718 16 a733 8 /********************************************************************************** This allocates a new string, and returns the name of the file that we have opened without any parent directory names or extensions. Note that if someone somehow has a / in the file name itself, they're hosed here. (and probably everywhere else as well) Specifically, the basename is what's in caps here: /foo/bar/BASENAME.extension BUG: should be returning a reply!!! **********************************************************************************/ - (Cstring) getBasename d735 49 a783 1 // Determine where the last / and . are. d785 2 a786 7 int lastSlash = strrpos(fileName, '/'); int lastDot = strrpos(fileName, '.'); int basenameSize, start, finish; Cstring tempName; // Determine the length of the basename by figuring out where its start is // (either right after the /, or at the start) and the end (before . or at the end) d788 4 a791 2 if (lastSlash == -1) start = 0; d793 1 a793 1 start = lastSlash+1; d795 2 a796 2 if ((lastDot == -1) || (lastDot < lastSlash)) finish = strlen(fileName); d798 9 a806 1 finish = lastDot; // don't subtract one, becasue counting like a strlen, above. d808 2 a809 6 basenameSize = finish - start; // Allocate the proper space for the basename, store the text, and return it. tempName = (Cstring) malloc(basenameSize+1); strncpy(tempName, &fileName[start], basenameSize); tempName[basenameSize] = (char)NULL; d814 11 a824 6 /********************************************************************************** This returns the current location in the file, which happens to be a direct mapping to the standard io routine: ftell. BUG: should be returning a reply? **********************************************************************************/ - (FilePos) getCurrentPosition d826 7 a832 3 if (theFile == NULL) /* should return a better reply */ return 0; d834 6 a839 1 return NXTell(theFile); d843 13 a855 7 /********************************************************************************** This gets the extension of the file name, which is the portion of its name that comes after the last . in its name. If there is no extension, a null string is returned. Specifically, the basename is what's in caps here: /foo/bar/basename.EXTENSION (if no extension, should return a reply with a positive error code and a null string) **********************************************************************************/ - (Cstring) getExtension; a856 2 // Determine where the last / and . are. We need the / to be sure we don't get a . further // up in the path somewhere. d858 10 a867 7 int lastSlash = strrpos(fileName, '/'); int lastDot = strrpos(fileName, '.'); int extensionSize, start; Cstring tempName; // Determine the length of the extension by figuring out where its start is // (after the .) d869 4 a872 2 if ((lastDot == -1) || (lastDot < lastSlash)) start = 0; d874 9 a882 8 start = lastDot+1; extensionSize = strlen(fileName) - start; // Allocate the proper space for the extension, store the text, and return it. tempName = (Cstring) malloc(extensionSize+1); strncpy(tempName, &fileName[start], extensionSize); tempName[extensionSize] = (char) NULL; d887 13 a899 4 /********************************************************************************** This gets the name of the current file, without the pathname attached to it. **********************************************************************************/ - (Cstring) getFilename d901 3 a903 1 // Determine where the last / is. d905 6 a910 9 int lastSlash = strrpos(fileName, '/'); int nameSize, start; Cstring tempName; // Determine the length of the filename by figuring out where its start is // (after the /) // if (lastSlash == -1) start = 0; d912 8 a919 8 start = lastSlash+1; nameSize = strlen(fileName) - start; // Allocate the proper space for the filename, store the text, and return it. tempName = (Cstring) malloc(nameSize+1); strncpy(tempName, &fileName[start], nameSize); tempName[nameSize] = (char)NULL; d924 13 a936 6 /********************************************************************************** this returns the path to the file that we are using without including the file name itself (it does include the terminal /). Note that if there are no /'s, lastSlash gets set to -1, and we return a null string **********************************************************************************/ - (Cstring) getDirectory a937 1 // Determine where the last / is. d939 13 a951 5 int lastSlash = strrpos(fileName, '/'); Cstring tempName; // Allocate the proper space for the filename, store the text, and return it. if (lastSlash != -1) d953 2 a954 3 tempName = (Cstring) malloc(lastSlash+1+1); //one for 0 origin, one for null strncpy(tempName, fileName, lastSlash+1); tempName[lastSlash+1] = (char) NULL; d957 5 a961 1 tempName=NULL; d966 12 a977 4 /********************************************************************************** this returns the full pathname of the file that we are using. **********************************************************************************/ - (Cstring) getPathname d979 9 a987 5 Cstring tempName; // Allocate the proper space for the pahtname, store the text, and return it. tempName = (Cstring) malloc(strlen(fileName)+1); strcpy(tempName, fileName); d1000 3 d1004 2 a1005 2 // Doc refers to exceptions being raised by NXSeek if bad things happen. Check for them!!!! // Should we be checking the result of NXTell also? (see ExceptionHandling in the doc) d1011 2 a1012 2 if (theFile == NULL) [self SetErrorCode: ERR_FILENOTOPEN d1014 2 a1015 2 else if (AccessType == FILE_APPEND) [self SetErrorCode: ERR_FILEAPPENDONLY d1019 26 a1044 3 NXSeek(theFile, byteLoc, NX_FROMSTART); [self SetErrorCode: ERR_OK AndText: "Moved OK"]; [self StorePositiveInteger: NXTell(theFile)]; d1064 1 a1064 1 - (Byte) readByte d1070 4 a1073 4 if (theFile == NULL) [self SetErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if ((AccessType == FILE_WRITEONLY) || (AccessType == FILE_APPEND)) [self SetErrorCode: ERR_FILEWRITEONLY d1077 2 a1078 2 byteIn = NXGetc(theFile); [self SetErrorCode: ERR_OK AndText: "Read the byte"]; d1098 1 a1098 1 // We don't store the EOF value yet... d1100 1 a1100 1 - (id) read: (PositiveInteger) numBytes bytesInto: (ByteString) buffer d1106 4 a1109 4 if (theFile == NULL) [self SetErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if ((AccessType == FILE_WRITEONLY) || (AccessType == FILE_APPEND)) [self SetErrorCode: ERR_FILEWRITEONLY d1113 1 a1113 1 BytesRead = NXRead(theFile, buffer, numBytes); d1118 1 a1118 1 [self SetErrorCode: ERR_READINGERROR AndText: "Read the wrong number of bytes"]; d1120 2 a1121 2 [self SetErrorCode: ERR_OK AndText: "Wrote the bytes."]; [self StorePositiveInteger: bytesWrote]; a1136 1 // Should we be flushing this? d1144 4 a1147 4 if (theFile == NULL) [self SetErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if (AccessType == FILE_READONLY) [self SetErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"]; d1150 1 a1150 1 byteOut = NXPutc(theFile, theByte); d1152 1 a1152 1 [self SetErrorCode: ERR_OK AndText: "Wrote the byte"]; d1154 1 a1154 1 [self SetErrorCode: ERR_WROTEBADCHAR AndText: "Wrote the wrong byte"]; d1171 1 a1171 2 // error. We always store the number of bytes we wrote. If we fail to flush all the // bytes we wrote, an error is also generated (the former error takes precedence, though) d1173 1 a1173 3 // NXFlush doesn't seem to actually flush mapped file streams, so I don't konw what // error it reports. Perhaps it should be removed entirely. Does NXSave... provide the // service I'm looking for? d1181 4 a1184 4 if (theFile == NULL) [self SetErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"]; else if (AccessType == FILE_READONLY) [self SetErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"]; d1187 1 a1187 2 bytesWrote = NXWrite(theFile, buffer, numBytes); bytesFlushed = NXFlush(theFile); d1192 1 a1192 1 [self SetErrorCode: ERR_WRITINGERROR a1193 3 else if (bytesFlushed != bytesWrote) [self SetErrorCode: ERR_FLUSHINGERROR AndText: "Flushed the wrong number of bytes"]; d1195 1 a1195 1 [self SetErrorCode: ERR_OK AndText: "Wrote the bytes."]; d1211 1 a1211 1 // Probably I should be using stat instead of playing games with NXTell and NXSeek d1218 1 a1218 1 if (theFile == NULL) d1220 1 a1220 1 [self SetErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened."]; d1222 1 d1226 5 a1230 5 currentLoc = NXTell(theFile); NXSeek(theFile, 0, NX_FROMEND); eofLoc = NXTell(theFile); NXSeek(theFile, currentLoc, NX_FROMSTART); [self SetErrorCode: ERR_OK AndText: "Obtained size of file."]; d1234 85 @ 1.4 log @Miscelaneous revisions. This is the last version of version 1. @ text @d4 2 a5 2 This is $Revision: 1.3 $ of this file It was last modified by $Author: death $ on $Date: 92/03/29 12:36:14 $ d8 3 d50 2 d53 1 d395 10 a404 3 // Routine: moveTo: // Parameters: the location to move to in the file // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) d406 1 a406 1 - (id) moveTo: (FilePos) byteLoc d408 1 a408 1 id result; d411 5 a415 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "The file wasn't even open! How d'you expect me to move around in it?!"]; d418 3 a420 3 NXSeek(theFile, 0, NX_FROMSTART); result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "We found what we NXsaught."]; d422 1 a422 1 return result; d427 12 a438 3 // Routine: readByte // Parameters: none // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) d440 1 a440 1 - (id) readByte d442 1 a442 1 id result; d444 2 d447 4 a450 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "Unable to read from unopened file. Nuke this object, start again."]; d453 4 a456 4 result = [[Reply alloc] initReplyWithCode: ERR_DOBETTERERRORCHECKING Text: "huh?" Data: (void*) NXGetc(theFile) Type: TYPE_CHAR]; d458 1 a458 2 return result; d462 13 a474 6 // Routine: readByte: // Parameters: tons // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) // NOTE david: there IS a need for a two value Reply object... // WAItaminute! What does the caller care about number of bytes read... // We should check... if bad number, then we return an error... or... d476 1 a476 1 - (id) read: (long int) numBytes bytesInto: (byte*) buffer retrieving: (long int*) bytesFound; d478 4 a481 3 id result; long junk2, junk3; d483 4 a486 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "closed file. no reading possible when the lights are out."]; d489 10 a498 5 junk2 = NXTell(theFile); junk3 = NXRead(theFile, buffer, numBytes); *bytesFound = junk3; result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "deludingonselferror"]; d500 1 a500 2 return result; d505 9 a513 3 // Routine: writeByte: // Parameters: none // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) flush?? d515 1 a515 1 - (id) writeByte: (byte) theByte; d517 1 a517 2 id result; char theChar; d519 2 d522 3 a524 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "Unable to read from unopened file. Nuke this object, start again."]; d527 6 a532 4 theChar = NXPutc(theFile, theByte); /* assure that theChar is the same as thebyte */ result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: ""]; d534 1 a534 2 return result; d540 14 a553 6 // Routine: writeByte: // Parameters: none // Bugs: header comment sucks... need better error checking. (e.g. check if at eof?) // NOTE david: there IS a need for a two valuereply object... // WAItaminute! What does the caller care about number of bytes written... // We should check... if bad number, then we return an error... d555 1 a555 1 - (id) write: (long int) numBytes bytesFrom: (byte*) buffer writing: (long int*) bytesWrote; d557 1 a557 1 id result; d559 2 d562 3 a564 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "closed file. no writing possible."]; d567 14 a580 5 *bytesWrote = NXWrite(theFile, buffer, numBytes); NXFlush(theFile); /* make sure number bytes returned == bytesWrote) */ /* assure that theChar is the same as thebyte */ result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "deludingonselferror"]; d582 1 a582 2 return result; d587 9 a595 2 // Routine: fileSize // Parameters: none d597 1 a597 1 - (id) fileSize d599 1 a599 3 id result; FilePos currentLoc, eofLoc; int junk; d601 1 d603 4 a606 2 result = [[Reply alloc] initErrorWithCode: ERR_FILENOTOPEN Text: "There's got to be a better way..."]; a608 1 /* yeah. I should probably use stat instead. heck. for now, though, this will suffice */ d613 2 a614 5 junk = NXTell(theFile); result = [[Reply alloc] initReplyWithCode: ERR_PEACHY Text: "got the size" Data: (void*) eofLoc Type: TYPE_ULONG]; d616 1 a616 1 return result; @ 1.3 log @Bug in the open method. If we failed, we returned OK, if we succed, we reported failure. @ text @d4 2 a5 2 This is $Revision: 1.2 $ of this file It was last modified by $Author: death $ on $Date: 92/03/29 12:29:06 $ d8 3 d25 1 a25 1 #import <stdio.h> d28 2 a29 1 d56 1 a56 1 - (id) openFile: (Cstring) workingName For: (long int) operation d59 3 a61 1 d69 19 a87 2 fileName = (Cstring) malloc(strlen(workingName) + 1); strcpy(fileName, workingName); d99 9 a107 2 theFile = NXMapFile(fileName, NX_WRITEONLY); break; d109 7 a115 1 theFile = NXMapFile(fileName, NX_READWRITE); d131 2 a132 1 if ((theFile == NULL) || (result == NULL)) d136 4 a139 2 result = [[Reply alloc] initErrorWithCode: ERR_PEACHY Text: "I found no errors (didn't look neither) so all must be OK."]; d147 1 a147 1 - (id) openFile: (Cstring) filename d153 11 d169 8 d178 3 d182 1 a186 1 d266 1 d315 1 d344 1 d354 1 a354 1 - (Cstring) getPath d366 1 d444 1 d451 3 a453 1 *bytesFound = NXRead(theFile, buffer, numBytes); d524 1 d536 1 @ 1.2 log @Oops. Managed to check in the wrong version. this one has the code reflecting the new location of the init methods @ text @d4 2 a5 2 This is $Revision: 1.1 $ of this file It was last modified by $Author: death $ on $Date: 92/03/29 12:18:44 $ d8 3 d95 1 a95 1 if ((theFile != NULL) && (result != NULL)) @ 1.1 log @Initial revision @ text @d4 2 a5 2 This is $Revision:$ of this file It was last modified by $Author: $ on $Date: $ d7 4 a10 1 * $Log:$ d53 1 a53 1 [super initialize]; d84 1 a84 1 result = [Reply initErrorWithCode: ERR_BADACCESS d93 1 a93 1 result = [Reply initErrorWithCode: ERR_CANTOPEN d96 1 a96 1 result = [Reply initErrorWithCode: ERR_PEACHY d133 1 a133 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d138 1 a138 1 result = [Reply initErrorWithCode: ERR_PEACHY d156 1 a156 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d161 1 a161 1 result = [Reply initErrorWithCode: ERR_PEACHY d330 1 a330 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d335 1 a335 1 result = [Reply initErrorWithCode: ERR_PEACHY d352 1 a352 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d356 1 a356 1 result = [Reply initReplyWithCode: ERR_DOBETTERERRORCHECKING d378 1 a378 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d383 1 a383 1 result = [Reply initErrorWithCode: ERR_PEACHY d402 1 a402 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d408 1 a408 1 result = [Reply initErrorWithCode: ERR_PEACHY d421 1 a421 1 // NOTE david: there IS a need for a two value Reply object... d430 1 a430 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d437 1 a437 1 result = [Reply initErrorWithCode: ERR_PEACHY d455 1 a455 1 result = [Reply initErrorWithCode: ERR_FILENOTOPEN d464 1 a464 1 result = [Reply initReplyWithCode: ERR_PEACHY @